#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <unistd.h>

/*定义互斥信号灯&机票总数变量*/
#define TICK_NUM 100
int ticketNum = TICK_NUM;
int mutexID;
pthread_t pth1, pth2, pth3;

union semun {
  int val;
  struct semid_ds *buf;
  unsigned short *array;
  struct seminfo *__buf;
} arg;

// P,V操作
void P(int semid, int index);
void V(int semid, int index);

//定义售票线程函数
void *subp();

int main() {
  //创建互斥信号灯
  mutexID = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
  if (mutexID == -1) printf("Failed to create semaphore!\n");
  //返回值：如果成功，返回信号灯集的IPC标识符。如果失败，返回-1。

  //信号灯赋初值
  arg.val = 1;
  if (semctl(mutexID, 0, SETVAL, arg) == -1)
    //返回值：如果成功，则为一个正数。
    // SETVAL设置信号灯集中的一个单独的信号灯的值。
    printf("Failed to initialize semaphore!\n");

  /*创建3个售票线程*/
  pthread_create(&pth1, NULL, subp, NULL);
  pthread_create(&pth2, NULL, subp, NULL);
  pthread_create(&pth3, NULL, subp, NULL);

  /*等待线程终止*/
  pthread_join(pth1, NULL);
  pthread_join(pth2, NULL);
  pthread_join(pth3, NULL);
  printf("Threads have already terminated!\n");
  //作用：挂起当前线程直到由参数th指定的线程被终止为止。

  /*删除信号灯*/
  semctl(mutexID, 0, IPC_RMID, arg);
  //如果不需要信号量，则从系统中删除它，此时使用semctl( )函数的IPC_RMID操作
  // IPC_RMID将信号灯集semid从内存中删除
  printf("Deleted semaphore successfully!\n");
  exit(0);
}
void P(int semid, int index) {
  struct sembuf sem;
  sem.sem_num = index;    // sem_num 将要处理的信号灯的下标
  sem.sem_op = -1;        // sem_op  要执行的操作
  sem.sem_flg = 0;        // sem_flg 操作标志,IPC_NOWAIT，一般设置0
  semop(semid, &sem, 1);  // 1:表示执行命令的个数
  return;
}
void V(int semid, int index) {
  struct sembuf sem;
  sem.sem_num = index;    // sem_num 将要处理的信号灯的下标
  sem.sem_op = 1;         // sem_op  要执行的操作
  sem.sem_flg = 0;        // sem_flg 操作标志,IPC_NOWAIT，一般设置0
  semop(semid, &sem, 1);  // 1:表示执行命令的个数
  return;
}
void *subp() {
  int soldNum = 0;
  pthread_t tid = pthread_self();  //获得线程自身ID
  //售票
  while (1) {
    usleep(10000);
    P(mutexID, 0);
    if (ticketNum) {
      soldNum++;
      printf("No.%ld Thread: No.%d Ticket is sold successfully!\n", tid,
             TICK_NUM - ticketNum + 1);
      printf(" %d tickets left\n", ticketNum);
      ticketNum--;
      V(mutexID, 0);
    } else {
      V(mutexID, 0);
      break;
    }
  }
  //售票结束
  printf("No.%ld Thread has sold %d tickets!\n", tid, soldNum);
}
